/****************************************************************************
 *
 * Copyright (C) 2003-2008 Los Alamos National Security, LLC
 *                         Packet Analytics Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.  You may not use, modify or
 * distribute this program under any other version of the GNU General
 * Public License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************/


package nfse;

import java.net.ServerSocket;
import java.util.*;
import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLServerSocketFactory;

/*******************************************************************************
 * This class is responsible for listening for connections from data clients
 * trying to stream data to this server. A DataSocket object is instantiated and
 * run upon each connection to the server.
 * 
 * @author
 * 
 * This code is the property of Los Alamos National Laboratory and the
 * University of California, Copyright 2005
 */
public class DataServer extends Thread {
    /***************************************************************************
     * The TCP port to listen on (1025-65535)
     */
    private int port = -1;

    /***************************************************************************
     * The number of records that have been processed by Net/FSE so far today
     */
    private long recordCount = 0;

    /***************************************************************************
     * The number of records that Net/FSE has dropped since the daily record
     * limit was exceeded.
     */
    private long thresholdExceededCount = 0;

    /***************************************************************************
     * The time at which the daily record limit was exceeded.
     */
    private java.sql.Timestamp thresholdExceededTS = null;

    /***************************************************************************
     * The number of records per day that can be processed by Net/FSE.
     */
    public static final long RECORD_THRESHOLD = 1000000;

    /***************************************************************************
     * A calendar with today's date. Used to determining when to reset the
     * record counts.
     */
    private GregorianCalendar today = null;

    private NetFSEServer parent = null;

    /***************************************************************************
     * Constructor to instantiate a new server.
     * 
     * @param portNum
     *            The port number on which to run the data server
     * 
     */
    public DataServer(int portNum, NetFSEServer parent) {
        this.parent = parent;
        this.port = portNum;

        today = new GregorianCalendar();
        today.setTimeInMillis(System.currentTimeMillis());
        today.set(Calendar.HOUR_OF_DAY, 0);
        today.set(Calendar.MINUTE, 0);
        today.set(Calendar.SECOND, 0);
        today.set(Calendar.MILLISECOND, 0);
        today.add(Calendar.DAY_OF_MONTH, 1);
        today.add(Calendar.SECOND, -1);

        String dateStr = today.get(Calendar.YEAR) + "-" + (today.get(Calendar.MONTH) + 1) + "-"
                + today.get(Calendar.DAY_OF_MONTH);
        try {
            MetaDatabase mdb = MetaDatabase.create();
            recordCount = mdb.getRecordCount(dateStr);
            thresholdExceededCount = mdb.getExceededCount(dateStr);
            mdb.closeConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public NetFSEServer getParent() {
        return parent;
    }

    public synchronized void updateRecordCounts() {
        String dateStr = today.get(Calendar.YEAR) + "-" + (today.get(Calendar.MONTH) + 1) + "-"
                + today.get(Calendar.DAY_OF_MONTH);
        try {
            MetaDatabase mdb = MetaDatabase.create();
            mdb.updateRecordCounts(dateStr, recordCount, thresholdExceededCount, thresholdExceededTS);
            mdb.closeConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /***************************************************************************
     * This function is called from within the DataSocket each time a record is
     * processed. It enforces the record threshold and maintains the record
     * count statistics.
     * 
     * @return Returns true if the record count has not been reached. Returns
     *         false when the threshold is exceeded.
     */
    public synchronized boolean incrementCount() {

        /***********************************************************************
         * Uncomment these lines to disable the record counting and threshold
         * enforcement.
         */
        // if (true)
        // return true;
        
        //if (recordCount >= RECORD_THRESHOLD) {
            /*******************************************************************
             * If the threshold has been exceeded, note the time at which it
             * first was exceeded. Return false.
             */
          //  if (thresholdExceededTS == null) {
            //    thresholdExceededTS = new java.sql.Timestamp(System.currentTimeMillis());
           // }
           // return false;
       // }
        recordCount++;
        if (recordCount % 50000 == 0) {
            /*******************************************************************
             * Periodically update the record count information in the metadata
             * database.
             */
            updateRecordCounts();
        }
        return true;
    }

    /***************************************************************************
     * This method is called to see if the day has changed from the last check.
     * When a new day is detected, the record threshold is reset, allowing
     * records to be processed again. This method is used to detect the change
     * in date.
     * 
     * @return True if the day has changed since last check. False if the day is
     *         the same as on the last check.
     */
    public synchronized boolean checkNewDay() {
        GregorianCalendar now = new GregorianCalendar();
        now.setTimeInMillis(System.currentTimeMillis());
        if (now.after(today)) {

            updateRecordCounts();

            today.set(Calendar.HOUR_OF_DAY, 0);
            today.set(Calendar.MINUTE, 0);
            today.set(Calendar.SECOND, 0);
            today.set(Calendar.MILLISECOND, 0);
            today.add(Calendar.DAY_OF_MONTH, 1);

            recordCount = 0;
            thresholdExceededCount = 0;
            thresholdExceededTS = null;
        } else {
            thresholdExceededCount++;
            if (thresholdExceededCount % 10000 == 0 || thresholdExceededCount == 1) {
                updateRecordCounts();
            }

            return false;
        }
        return true;
    }

    /***************************************************************************
     * This method is run on server startup by the NetFSEServer object. It
     * creates a new DataSocket and runs it each time a data client connects.
     */
    public void run() {
        try {

            System.out.println("Data Server: Listening on port " + port + ".");
            ServerSocketFactory ssf = null;
            if (NetFSE.USE_SSL_DATA)
                // Use SSL sockets
                ssf = SSLServerSocketFactory.getDefault();
            else
                // Use a plaintext socket
                ssf = ServerSocketFactory.getDefault();

            DataSocketMonitor dsm = new DataSocketMonitor(60);
            dsm.start();

            ServerSocket ss = ssf.createServerSocket(port);
            while (true) {
                try {
                    /***********************************************************
                     * Create a new DataSocket, wait for connection from client.
                     */
                    DataSocket ds = new DataSocket(ss.accept(), this);

                    /***********************************************************
                     * Create a new DataSocket, wait for connection from client
                     */
                    dsm.addDataSocket(ds);

                    /***********************************************************
                     * Run the DataSocket thread upon client connection
                     */
                    ds.start();

                } catch (Exception E) {
                    E.printStackTrace();
                }
            }
        } catch (Exception E) {
            E.printStackTrace();
        }
    }

}